using UnityEngine;
using System;
using System.Collections;

//[RequireComponent (typeof (Navigator))
public class Robot : MonoBehaviour {
	
	Job currentJob;
	public bool hasAJob;
	public double pressureInTank;
	bool addedStatustoList;
	bool atJobSite;
	double x, y, z;
	int jobX, jobY, jobZ;
	
	private ArrayList path;
	private int pathIndex;
	Point nextPoint;
	NavMeshAgent agent;
	
	double rateOfMovement;
	
	public Tank tank;
	Torso torso;
	Legs legs;
	Arms arms;
	Head head;
	
	public bool hasMiningTool, hasConstructionTool, hasDeconstructionTool;
	public double rateOfConstruction, rateOfDeconstruction, rateOfMining;
	public ArrayList tools;
	public MiningTool miningTool;
	public ConstructionTool constructionTool;
	public DeconstructionTool deconstructionTool;
	
	public Item hauledItem;
	public bool hauling;
	
	// Use this for initialization
	void Start () {
		hasAJob = false;
		currentJob = null;
		pressureInTank = 100000;
		nextPoint = new Point(0,0,0);
		
		hasMiningTool = false;
		hasConstructionTool = false;
		hasDeconstructionTool = false;
		
		rateOfConstruction = 1;
		rateOfMining = 1;
		rateOfDeconstruction = 1;
		
		tools = new ArrayList();
		
		x = (double)transform.position.x;
		y = (double)transform.position.y;
		z = (double)transform.position.z;
		
		hauling = false;
		
		agent = (NavMeshAgent)GetComponent("NavMeshAgent");
		
	}
	
	// Update is called once per frame
	void Update(){
		if (!hasAJob){
			if (pressureInTank < 500){
				addedStatustoList = true;
				//refill with steam
				Job j = new RechargingJob((BoilerManager)ConstructionController.boilers[0]);
				Controller.removeJob(j);
				this.assignJob(j);
			}
			
			if (!hasConstructionTool | !hasDeconstructionTool | !hasMiningTool){
				if (!hasConstructionTool){
					foreach (Tool t in Controller.getUnusedTools()){
						if ((t.isConstructionTool) & (!addedStatustoList)){
							addedStatustoList = true;
							currentJob = new EquipJob(t, t.x, t.y, t.z);
							t.updatePosition();
						}
					}
				} if (!hasDeconstructionTool){
					foreach (Tool t in Controller.getUnusedTools()){
						if ((t.isDeconstructionTool) & (!addedStatustoList)){
							addedStatustoList = true;
							currentJob = new EquipJob(t, t.x, t.y, t.z);
							t.updatePosition();
						}
					}
				} if (!hasMiningTool){
					foreach (Tool t in Controller.getUnusedTools()){
						if ((t.isMiningTool) & (!addedStatustoList)){
							addedStatustoList = true;
							assignJob(new EquipJob(t, t.x, t.y, t.z));
							t.updatePosition();
						}
					}
				}
			}
			
			if (!addedStatustoList){
				addedStatustoList = true;
				Controller.addRobot(this);
			}
		} 
	}
	
	
	void FixedUpdate () {
		
		//Deal with rendering of tools
		if (hasMiningTool){
			miningTool.item.transform.position = new Vector3((float)x - 1, (float)y, (float)z);
			miningTool.updatePosition();
		}
		
		if (atJobSite & hasAJob){
			bool done = false;
			if (currentJob.jobType == 1){
				done = ((BuildingOperationJob)(currentJob)).workOn(1, this);
			} else if (currentJob.jobType == 2){
				done = ((ConstructionJob)(currentJob)).workOn(1, this);
				if (hasDeconstructionTool){
					ParticleEmitter p = (ParticleEmitter)(constructionTool.item.GetComponent("ParticleEmitter"));
					p.emit = true;
				}
			} else if (currentJob.jobType == 3){
				done = ((DeconstructionJob)(currentJob)).workOn(1, this);
				if (hasDeconstructionTool){
					
				}
			} else if (currentJob.jobType == 4) {
				done = ((EquipJob)(currentJob)).workOn(this);
				done = true;
			} else if (currentJob.jobType == 5){
				done = ((HaulingJob)(currentJob)).workOn(this);
			} else if (currentJob.jobType == 6){
				done = ((MiningJob)(currentJob)).workOn(1, this);
				if (hasMiningTool){
					ParticleEmitter p = (ParticleEmitter)(miningTool.item.GetComponent("ParticleEmitter"));
					p.emit = true;
				}
			} else if (currentJob.jobType == 7){
				done = ((RechargingJob)(currentJob)).workOn(this);
			} else if (currentJob.jobType == 8){
				done = ((SmeltOre)(currentJob)).workOn(1, this);
			} else if (currentJob.jobType == 9){
				done = ((PlacingJob)(currentJob)).workOn(this);
			} else if (currentJob.jobType == 10){
				done = ((SmeltBronze)(currentJob)).workOn(1, this);
			} else if (currentJob.jobType == 11){
				done = ((SmeltBrass)(currentJob)).workOn(1, this);
			} else if (currentJob.jobType == 12){
				done = ((SmeltGalvanizedSteel)(currentJob)).workOn(1, this);
			} else if (currentJob.jobType == 13){
				done = ((SmeltSteel)(currentJob)).workOn(1, this);
			} else if (currentJob.jobType == 14){
				done = ((ForgeRobotComponents)(currentJob)).workOn(1, this);
			} else if (currentJob.jobType == 15){
				done = ((AssembleRobot)(currentJob)).workOn(1, this);
			} else {
				done = true;
				print ("Snork");
			}
			if (done){
				Controller.finishedJob(this, currentJob);
				currentJob = null;
				hasAJob = false;
				addedStatustoList = false;
			}
			
		}
		if (hasAJob & !atJobSite){
			rateOfMovement = 0;
			foreach (Tool t in tools){
				rateOfMovement += (float)(t.material.weightModifier);
			}
			
			rateOfMovement = (0.05 / (rateOfMovement + 1));
			agent.speed = (float)rateOfMovement;
			
			try {
				nextPoint = (Point)(path[pathIndex]);
			} catch {
				Debug.LogError("Cannot find path");
				Controller.finishedJob(this, currentJob);
			}
			bool goodToMoveOn = true;
			pathTo(nextPoint.x, nextPoint.y, nextPoint.z, rateOfMovement);
			if((int)(x) != nextPoint.x){
				goodToMoveOn = false;
			} else if((int)(y) != nextPoint.y){
				goodToMoveOn = false;
			} else if((int)(z)!= nextPoint.z){
				goodToMoveOn = false;
			}
			
			if (goodToMoveOn){
				pathIndex += 1;
			}
			if ((((int)x == jobX) & ((int)y == jobY) & ((int)z == jobZ)) | (pathIndex == path.Count)){
				x = (int) x;
				y = (int) y;
				z = (int) z;
				atJobSite = true;
				pathIndex = 0;
			}
			
			
			
		}
		
		if (hauling){
			hauledItem.setPosition(new Vector3(transform.position.x, transform.position.y + 1f, transform.position.z + 1f));
		}
		
	}
	
	public void starterate(Arms narms, Legs nlegs, Head nhead, Torso ntorso, Tank ntank){
		arms = narms;
		legs = nlegs;
		head = nhead;
		torso = ntorso;
		tank = ntank;
	}
	
	public void addConstructionTool(ConstructionTool tool){
		hasConstructionTool = true;
		rateOfConstruction = tool.speedWithTool;
		tools.Add(tool);
		constructionTool = tool;
	}
	
	public void addDeconstructionTool(DeconstructionTool tool){
		hasDeconstructionTool = true;
		rateOfDeconstruction = tool.speedWithTool;
		tools.Add(tool);
		deconstructionTool =  tool;
	}
	
	public void addMiningTool(MiningTool tool){
		hasMiningTool = true;
		rateOfMining = tool.speedWithTool;
		tools.Add(tool);
		miningTool = tool;
	}
	
	public void assignJob(Job j){
		if (!hasAJob){
			currentJob = j;
			hasAJob = true;
			atJobSite = false;
			jobX = j.x;
			jobY = j.y;
			jobZ = j.z;
			
			Controller.removeRobot(this);
			Controller.removeJob(currentJob);
			
			x = (double)transform.position.x;
			y = (double)transform.position.y;
			z = (double)transform.position.z;
			
			x = (int) x;
			y = (int) y;
			z = (int) z;
			
			//GetComponent<Navigator>().targetPosition = m_Target.position;
			//agent.destination = new Vector3((float)jobX, (float)jobY, (float)jobZ);
			path = generatePath(new Point((int)(x), (int)(y), (int)(z)), new Point((int)(jobX), (int)(jobY), (int)(jobZ)));
		} else {
			Controller.addJob(j);
			addedStatustoList = true;
			Debug.Log("I already have a job");
		}
	}
	
	public void assignJob(Job j, bool t){
		currentJob = j;
		hasAJob = true;
		atJobSite = false;
		jobX = j.x;
		jobY = j.y;
		jobZ = j.z;
		
		Controller.removeRobot(this);
		Controller.removeJob(currentJob);
		
		x = (double)transform.position.x;
		y = (double)transform.position.y;
		z = (double)transform.position.z;
		
		x = (int) x;
		y = (int) y;
		z = (int) z;
		
		//GetComponent<Navigator>().targetPosition = m_Target.position;
		//agent.destination = new Vector3((float)jobX, (float)jobY, (float)jobZ);
		path = generatePath(new Point((int)(x), (int)(y), (int)(z)), new Point((int)(jobX), (int)(jobY), (int)(jobZ)));
	}
	
	ArrayList generatePath(Point startLoc, Point goalLoc){
		ArrayList open = new ArrayList();
		ArrayList attemptingPath = new ArrayList();
		ArrayList triedPoints = new ArrayList();
		
		open.Add(startLoc);
		attemptingPath.Add(startLoc);
		
		Point pointToTry = startLoc;
		Point bestSoFar = startLoc;
		
		while (!pointToTry.isEqualTo(goalLoc)){
			
			// Generates points next to current one
			Point[] adjacentpoints = pointToTry.getAdjacentPoints3d();
			// Finds one with lowest cost, and goes from there
			int triedNum = 0;
			bool okay = true;
			for (int i = 0; i < 26; i++){
				if (adjacentpoints[i].getFCost(goalLoc) < bestSoFar.getFCost(goalLoc) + 1){
					
					foreach(Point p in triedPoints){
						if (p.isEqualTo(adjacentpoints[i])){
							okay = false;
						}
					}
					
					if ((!adjacentpoints[i].occupied)&(okay)){
						bestSoFar = adjacentpoints[i];
					} else {
						triedNum += 1;
					}
					
				}
			}
			
			if (triedNum > 24){
				attemptingPath = null;
				attemptingPath = new ArrayList();
				Debug.LogError("Cannot find path - too many obstacles");
				return attemptingPath;
			}
			
			if (bestSoFar.isEqualTo(startLoc)){
				//attemptingPath = null;
				//attemptingPath = new ArrayList();
				//Debug.LogError("Cannot find path - invalid start");
				//return attemptingPath;
			} else {
				triedPoints.Add(bestSoFar);
			}
			
			pointToTry = bestSoFar;
			attemptingPath.Add(pointToTry);
			triedPoints.Add(pointToTry);
			
		}
		return attemptingPath;
		
		
	}
	
	void pathTo(int pathX, int pathY, int pathZ, double travelSpeed){
		if ((int)x < pathX){
			x +=  travelSpeed;	
		} else if (x > pathX){
			x -= travelSpeed;
		}
		if ((int)y < pathY){
			y += travelSpeed;	
		} else if ((int)y > pathY){
			y -= travelSpeed;
		}
		if ((int)z < pathZ){
			z += travelSpeed;	
		} else if ((int)z > pathZ){
			z -= travelSpeed;
		}
		
		pressureInTank -= 1;
		
		transform.position = new Vector3((float)x, (float)y, (float)z);
		
	}
	
	public Point getPostion(){
		Point p = new Point((int)(x), (int)(y), (int)(z));
		return p;
	}
	
	
}